//
//  SVGElement.m
//  Shake and Bake
//
//  Created by Antoine Quint on 12/08/07.
//  Copyright 2007 __MyCompanyName__. All rights reserved.
//

#import "SVGElement.h"
#import "Canvas.h"
#import "CoreServices/CoreServices.h"

@implementation SVGElement

NSDictionary *COLORS;

- (id)initWithAttributeDict:(NSDictionary *)attributesDict andView:(Canvas *)aView
{
  view = aView;
	attributes = [attributesDict copy];
  [super init];
	if (self) {
		NSString *attribute;
		
		// check if we have an id set
		attribute = [attributes objectForKey:ATTRIBUTE_NAME_ID];
		if (attribute != nil) {
      [[view ids] setObject:self forKey:attribute];
		}
		
		attribute = [attributes objectForKey:ATTRIBUTE_NAME_FILL];
		fillColor = ([attribute isEqualToString:@"none"]) ? nil : [SVGElement parseColor:attribute];

		attribute = [attributes objectForKey:ATTRIBUTE_NAME_STROKE];
		strokeColor = (attribute == nil || [attribute isEqualToString:@"none"]) ? nil : [SVGElement parseColor:attribute];
		
		attribute = [attributes objectForKey:ATTRIBUTE_NAME_STROKE_WIDTH];
		strokeWidth = (attribute == nil) ? 1 : [attribute floatValue];
		
		attribute = [attributes objectForKey:ATTRIBUTE_NAME_OPACITY];
		groupOpacity = (attribute == nil) ? 1 : [attribute floatValue];

		attribute = [attributes objectForKey:ATTRIBUTE_NAME_FILL_OPACITY];
		fillOpacity = (attribute == nil) ? 1 : [attribute floatValue];

		attribute = [attributes objectForKey:ATTRIBUTE_NAME_STROKE_OPACITY];
		strokeOpacity = (attribute == nil) ? 1 : [attribute floatValue];

		attribute = [attributes objectForKey:ATTRIBUTE_NAME_TRANSFORM];
		if (attribute != nil) {
      self.transform = [SVGElement parseTransform:attribute];
		}  	

		// set core properties
		self.opacity = groupOpacity;
		self.anchorPoint = CGPointMake(0.0f, 0.0f);

		// tell the layer to display
		[self setNeedsDisplay];
	}
	return self;
}

/***** PARSING MATRIX *****/

+ (CATransform3D)parseTransform:(NSString *)attribute
{
  // start with an identity matrix
  CATransform3D transform = CATransform3DIdentity;
  // set up parser params
  int i;
	char currentChar;
  BOOL parsingCommand = YES;
  NSString *emptyString = @"";
	NSMutableString *currentCommand = [NSMutableString string];
	NSMutableString *currentParam = [NSMutableString string];
	NSMutableArray *params = [NSMutableArray arrayWithCapacity:0];
  // begin token parsing
	for (i = 0; i < [attribute length]; i++) {
		currentChar = [attribute characterAtIndex:i];
		if (parsingCommand) {
      // start quote means our command is complete
      if (currentChar == '(') {
        parsingCommand = NO;
      }
      else if (currentChar != ' ') {
  			[currentCommand appendFormat:@"%c", currentChar];
      }
		}
		else {
  		if (currentChar == ',' || currentChar == ' ') {
        if (![currentParam isEqualToString:emptyString]) {
    			[params addObject:currentParam];
    			currentParam = [NSMutableString string];
        }
  		}
  		// command is over
      else if (currentChar == ')') {
    		[params addObject:currentParam];
    		// do something with that command
        transform = [SVGElement updateTransform:transform withCommand:currentCommand andParams:params];
    		// reset
        currentCommand = [NSMutableString string];
        currentParam = [NSMutableString string];
        params = [NSMutableArray arrayWithCapacity:0];
        // note we're now looking for a command
        parsingCommand = YES;
      }
  		else {
  			[currentParam appendFormat:@"%c", currentChar];
  		}
		}    		
	}
	return transform;
}

+ (CATransform3D)updateTransform:(CATransform3D)transform withCommand:(NSString *)command andParams:(NSArray *)params
{
  if ([command isEqualToString:@"translate"]) {
    float tx = [[params objectAtIndex:0] floatValue];
    float ty = 0.0f;
    if ([params count] == 2) {
      ty = [[params objectAtIndex:1] floatValue];
    }
    NSLog(@"added translate with values: %f, %f", tx, ty);
    transform = CATransform3DTranslate(transform, tx, ty, 0.0f);
  }
  else if ([command isEqualToString:@"rotate"]) {
    float angle = [[params objectAtIndex:0] floatValue] / 180.0 * pi;
    float cx = 0;
    float cy = 0;
    if ([params count] == 3) {
      cx = [[params objectAtIndex:1] floatValue];
      cy = [[params objectAtIndex:2] floatValue];
    }
    NSLog(@"added rotate with values: %f, %f, %f", angle, cx, cy);
    transform = CATransform3DTranslate(transform, cx, cy, 0.0f);
    transform = CATransform3DRotate(transform, angle, 0.0f, 0.0f, 1.0f);
    transform = CATransform3DTranslate(transform, -cx, -cy, 0.0f);
  } 
  else if ([command isEqualToString:@"scale"]) {
    float sx = [[params objectAtIndex:0] floatValue];
    float sy = sx;
    if ([params count] == 2) {
      sy = [[params objectAtIndex:1] floatValue];
    }
    NSLog(@"added scale with values: %f, %f", sx, sy);
    transform = CATransform3DScale(transform, sx, sy, 1.0f);
  }
  return transform;
}

/***** COLOR PARSING *****/

+ (NSColor *)parseColor:(NSString *)color
{
	
	// load the colors dictionary if it's not done yet
	if (COLORS == nil) {
		COLORS = [NSDictionary dictionaryWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"colors" ofType:@"plist"]];
	}
	
	NSColor *resultColor;
	NSRange range = [color rangeOfString:@"rgb("];	
	if (range.length && range.location == 0) {
		resultColor = [self parseRGBColor:color];
	}
	else {
		range = [color rangeOfString:@"#"];	
		if (range.length && range.location == 0) {
			resultColor = [self parseHexaColor:color];
		}
		else {
			range = [color rangeOfString:@"url("];	
			if (range.length && range.location == 0) {
				resultColor = [NSColor colorWithDeviceRed:0 green:0 blue:0 alpha:1];
			}
			else {
				id valueInColorsDict = [COLORS objectForKey:color];
				if (valueInColorsDict) {
					resultColor = [self parseRGBColor:valueInColorsDict];
				}
				else {
					resultColor = [NSColor colorWithDeviceRed:0 green:0 blue:0 alpha:1];
				}
			}
		}
	}
	return resultColor;
}

// TODO: handle % and white spaces
+ (NSColor *)parseRGBColor:(NSString *)color
{
	NSMutableArray *components = [NSMutableArray arrayWithCapacity:3];	
	int i;
	char currentChar;
	NSMutableString *currentColor = [NSMutableString string];  
	for (i = 4; i < [color length]; i++) {
		currentChar = [color characterAtIndex:i];
		if (currentChar == ',') {
			[components addObject:currentColor];
			currentColor = [NSMutableString string];
		}
		else {
			[currentColor appendFormat:@"%c", currentChar];
		}
	}
	[components addObject:currentColor];
	float r = [[components objectAtIndex:0] floatValue] / 255;
	float g = [[components objectAtIndex:1] floatValue] / 255;
	float b = [[components objectAtIndex:2] floatValue] / 255;
	return [NSColor colorWithDeviceRed:r green:g blue:b alpha:1];
} 

+ (NSColor *)parseHexaColor:(NSString *)color
{
	int r, g, b;
	int length = [color length];
	// #fff form
	if (length == 4) {
		r = [self parseHexaChar:[color characterAtIndex:1]] * 16 + [self parseHexaChar:[color characterAtIndex:1]];
		g = [self parseHexaChar:[color characterAtIndex:2]] * 16 + [self parseHexaChar:[color characterAtIndex:2]];
		b = [self parseHexaChar:[color characterAtIndex:3]] * 16 + [self parseHexaChar:[color characterAtIndex:3]];
	}
	// #ffffff form
	else if (length == 7) {
		r = [self parseHexaChar:[color characterAtIndex:1]] * 16 + [self parseHexaChar:[color characterAtIndex:2]];
		g = [self parseHexaChar:[color characterAtIndex:3]] * 16 + [self parseHexaChar:[color characterAtIndex:4]];
		b = [self parseHexaChar:[color characterAtIndex:5]] * 16 + [self parseHexaChar:[color characterAtIndex:6]];
	}
	return [NSColor colorWithDeviceRed:((float)r/255) green:((float)g/255) blue:((float)b/255) alpha:1];
}

+ (int)parseHexaChar:(char)c
{
	int result = -1;
	if (c == 'a') {
		result = 10;
	} else if (c == 'b') {
		result = 11;
	} else if (c == 'c') {
		result = 12;
	} else if (c == 'd') {
		result = 13;
	} else if (c == 'e') {
		result = 14;
	} else if (c == 'f') {
		result = 15;
	} else { 
		result = [[NSString stringWithFormat:@"%c", c] intValue];
	}
	return result;
}

/***** RENDERING *****/

- (void)makePath:(CGContextRef)context
{
}

- (void)drawInContext:(CGContextRef)context
{
	// draw the fill
	if (fillColor) {
		[self makePath:context];
		CGContextSetRGBFillColor(context, [fillColor redComponent], [fillColor greenComponent], [fillColor blueComponent], fillOpacity); 
		CGContextFillPath(context);
	}
	// draw the stroke
	if (strokeColor) {
		[self makePath:context];
		CGContextSetRGBStrokeColor(context, [strokeColor redComponent], [strokeColor greenComponent], [strokeColor blueComponent], strokeOpacity); 
		CGContextSetLineWidth(context, strokeWidth);
		CGContextStrokePath(context);
	}
}

@end
